package discover

import (
	"fmt"
	"log"
	"strconv"
	"sync"

	"github.com/go-kit/kit/sd/consul"
	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/api/watch"
)

//基于go-kit实现Consul服务发现Client
//实现DiscoveryClient接口
type KitDiscoverClient struct {
	Host   string
	Port   int
	client consul.Client

	//服务实例信息缓存
	config       *api.Config
	mutex        sync.Mutex
	instancesMap sync.Map
}

//初始化一个服务发现客户端
func NewKitDiscoverClient(consulHost string, consulPort int) (DiscoveryClient, error) {
	consulConfig := api.DefaultConfig()
	consulConfig.Address = consulHost + ":" + strconv.Itoa(consulPort)
	apiClient, err := api.NewClient(consulConfig)
	if err != nil {
		return nil, err
	}
	client := consul.NewClient(apiClient)

	//初始化好相关配置Config和Client后进行直接赋值
	return &KitDiscoverClient{
		Host:   consulHost,
		Port:   consulPort,
		client: client,
		config: consulConfig,
	}, err
}

//服务注册
func (cc *KitDiscoverClient) Register(serviceName, instanceId, healthCheckUrl string, instanceHost string, instancePort int, meta map[string]string, logger *log.Logger) bool {
	//服务实例元数据
	serviceRegistration := &api.AgentServiceRegistration{
		ID:      instanceId,   //服务ID
		Name:    serviceName,  //服务名
		Address: instanceHost, //服务Host
		Port:    instancePort, //服务端口号
		Meta:    meta,
		Check: &api.AgentServiceCheck{
			DeregisterCriticalServiceAfter: "15s",
			// HTTP:                           "http://" + instanceHost + ":" + strconv.Itoa(instancePort) + healthCheckUrl,
			Interval: "5s",
			GRPC:     fmt.Sprintf("%s:%s", instanceHost, strconv.Itoa(instancePort)), //采用GRPC检测
			Timeout:  "5s",
		},
	}
	//服务注册
	err := cc.client.Register(serviceRegistration)
	if err != nil {
		logger.Println("[Kit] Register Service Error!")
		return false
	}
	logger.Println("[Kit] Register Service Success!")
	return true
}

//实现DiscoveryClient注销方法
func (cc *KitDiscoverClient) DeRegister(instanceId string, logger *log.Logger) bool {
	//构建包含服务实例ID的元数据结构体
	serviceRegistration := &api.AgentServiceRegistration{
		ID: instanceId,
	}
	//直接调用注销方法即可
	err := cc.client.Deregister(serviceRegistration)
	if err != nil {
		logger.Println("[Kit] DeRegister Service Error! Detail:", err.Error())
		return false
	}
	logger.Println("[Kit] DeRegister Service Success!")
	return true
}

//实现DiscoveryClient服务发现方法
func (cc *KitDiscoverClient) DiscoverServices(serviceName string, logger *log.Logger) []interface{} {
	//对该服务已经监控并缓存的进行获取
	instanceList, ok := cc.instancesMap.Load(serviceName)
	if ok {
		return instanceList.([]interface{})
	}
	//未监控与缓存情况如下
	//申请锁
	cc.mutex.Lock()
	//再一次判断是否已缓存，是的话直接返回，否则进行下一步
	instanceList, ok = cc.instancesMap.Load(serviceName)
	if ok {
		return instanceList.([]interface{})
	} else {
		//监控进程
		go func() {
			params := make(map[string]interface{})
			params["type"] = "service"
			params["service"] = serviceName
			plan, _ := watch.Parse(params)
			plan.Handler = func(u uint64, i interface{}) { //监控到变化并且更新缓存的处理方法
				if i == nil {
					return
				}
				v, ok := i.([]*api.ServiceEntry)
				if !ok {
					return //数据异常
				}
				//没有服务实例在线
				if len(v) == 0 {
					cc.instancesMap.Store(serviceName, []interface{}{})
				}
				var healthServices []interface{}
				//若有变更且服务正常则进行缓存
				for _, service := range v {
					if service.Checks.AggregatedStatus() == api.HealthPassing {
						healthServices = append(healthServices, service.Service)
					}
				}
				cc.instancesMap.Store(serviceName, healthServices)
			}
			defer plan.Stop()
			plan.Run(cc.config.Address)
		}()
	}
	defer cc.mutex.Unlock()

	//根据服务名请求服务实例列表，可以添加筛选参数
	entries, _, err := cc.client.Service(serviceName, "", false, nil)
	if err != nil {
		cc.instancesMap.Store(serviceName, []interface{}{})
		logger.Println("Discovery Service Error.")
		return nil
	}
	instances := make([]interface{}, 0, len(entries))
	for _, instance := range entries {
		instances = append(instances, instance)
	}
	//获取到的实例列表进行缓存
	cc.instancesMap.Store(serviceName, instances)
	return instances
}
